﻿using System;
using System.Collections;
using System.Data.Entity;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Linq;

namespace DAL.Model
{
    /// <summary>
    /// Exposes feature to handle changed objects and manage transaction
    /// </summary>
    public class MasterSession : MasterEDM
    {
        #region Variables

        private readonly DbContextTransaction _contextTransaction;

        #endregion

        #region Constructor

        public MasterSession()
        {
            _contextTransaction = Database.BeginTransaction();
        }

        #endregion

        #region Public methods

        public T Clone<T>(T source) where T : EntityObject
        {
            var obj = new System.Runtime.Serialization.DataContractSerializer(typeof (T));
            using (var stream = new System.IO.MemoryStream())
            {
                obj.WriteObject(stream, source);
                stream.Seek(0, System.IO.SeekOrigin.Begin);
                return (T) obj.ReadObject(stream);
            }
        }

        public EntityObject LoadAllChild(EntityObject source)
        {
            var propList = (from a in source.GetType().GetProperties()
                            where a.PropertyType.Name == "EntityCollection`1"
                            select a).ToList();
            foreach (var prop in propList)
            {
                var instance = prop.GetValue(source, null);
                var isLoad = (bool) instance.GetType()
                                        .GetProperty("IsLoaded")
                                        .GetValue(instance, null);
                if (isLoad)
                {
                    continue;
                }
                var mi = (from a in instance.GetType().GetMethods()
                          where a.Name == "Load" && a.GetParameters().Length == 0
                          select a)
                    .FirstOrDefault();

                mi.Invoke(instance, null);
            }
            return source;
        }

        public EntityObject ClearEntityReference(EntityObject source,
                                                 bool bcheckHierarchy)
        {
            return ClearEntityObject(source, bcheckHierarchy);
        }

        private T ClearEntityObject<T>(T source, bool bcheckHierarchy)
            where T : class
        {
            //Throw if passed object has nothing
            if (source == null)
            {
                throw new Exception("Null Object cannot be cloned");
            }
            // get the TYpe of passed object 
            var tObj = source.GetType();
            // check object Passed does not have entity key Attribute 
            if (tObj.GetProperty("EntityKey") != null)
            {
                tObj.GetProperty("EntityKey").SetValue(source, null, null);
            }

            //bcheckHierarchy this flag is used to check
            //and clear child object releation keys 
            if (!bcheckHierarchy)
            {
                return source;
            }

            // Clearing the Entity for Child Objects 
            // Using the Linq get only Child Reference objects   from source object 
            var propList = (from a in source.GetType().GetProperties()
                            where a.PropertyType.Name.ToUpper() == "ENTITYCOLLECTION`1"
                            select a).ToList();

            // Loop thorough List of Child Object and Clear the Entity Reference 
            foreach (var prop in propList)
            {
                var keys = (IEnumerable) tObj.GetProperty(prop.Name)
                                             .GetValue(source, null);

                foreach (var key in keys)
                {
                    //Clearing Entity Reference from Parnet Object
                    var ochildprop = (from a in key.GetType().GetProperties()
                                      where a.PropertyType.Name == "EntityReference`1"
                                      select a).SingleOrDefault();

                    ClearEntityObject(ochildprop.GetValue(key, null), (false));

                    //Clearing the the Entity Reference from
                    //Child object .This will recrusive action
                    ClearEntityObject(key, true);
                }
            }
            return source;
        }

        public void CommitChanges()
        {
            //try
            //{
            if (_contextTransaction == null)
            {
                return;
            }
            SaveChanges();
            _contextTransaction.Commit();
            //}
            //catch (DbEntityValidationException dbEx)
            //{
            //    foreach (var validationErrors in dbEx.EntityValidationErrors)
            //    {
            //        foreach (var validationError in validationErrors.ValidationErrors)
            //        {
            //            Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
            //        }
            //    }
            //} 
        }

        #endregion
    }
}
